﻿using Deployment.Data.Environment.Connection;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;


namespace Deployment.Data.Environment
{
    public class ConnectionXmlSerializer<T> : IXmlSerializable where T : AbstractConnection
    {
        private const string TYPE_ATTRIB = "Type";

        private const string NAME_ATTRIB = "Name";
        private const string DATA_ELEMENT = "Data";

        private T _data;

        public ConnectionXmlSerializer() { }

        public ConnectionXmlSerializer(T data)
        {
            this.Data = data;
        }

        /// <summary>
        /// [Concrete] Data to be stored/is stored as XML.
        /// </summary>
        public T Data
        {
            get { return _data; }
            set { _data = value; }
        }

        #region IXmlSerializable members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            string ElementName = reader.Name;
            //Cast the data back from the Abstract Type
            string typeAttrib = reader.GetAttribute(TYPE_ATTRIB);

            string nameAttrib = reader.GetAttribute(NAME_ATTRIB);
            
            string dataElement = null;
            while (true)
            {
                reader.Read();
                if (reader.NodeType == System.Xml.XmlNodeType.Element)
                {
                    if (reader.Name.Equals(DATA_ELEMENT))
                    {
                        dataElement = reader.ReadElementContentAsString();
                        reader.ReadEndElement();
                        break;
                    }
                }
                else if (reader.NodeType == System.Xml.XmlNodeType.EndElement && reader.Name.Equals(ElementName))
                {
                    break;
                }
            }
            
            

            //ensure type is specified
            if (string.IsNullOrEmpty(typeAttrib))
                throw new ArgumentException("Unable to read xml data for abstract type [" + typeof(T).Name +
                    "] because no 'Type' attribute was not specified in the element.");
            
            Type type = Type.GetType(typeAttrib);
            //Check that we found the type
            if (type == null)
                throw new InvalidCastException("Unable to create Connection of type [" + typeAttrib + "], because it was not found.");

            //Confirm the type inherits from Abstract Connection
            if (!type.IsSubclassOf(typeof(T)))
                throw new InvalidCastException("Connection of type [" + type.Name +"] cannot be used because it does not inherit from " + typeof(T).Name + ".");

            //reader.ReadStartElement();
            //this.Data = (T)new 
            //    XmlSerializer(type).Deserialize(reader);
            //reader.ReadEndElement();

            T obj = (T)Activator.CreateInstance(type);
            obj.Name = nameAttrib;
            obj.Data = dataElement;
            Data = obj;
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            // Write the Type Name to the XML Element as an Attrib and Serialize
            Type type = _data.GetType();

            // BugFix: Assembly must be FQN since Types can/are external to current.
            //writer.WriteAttributeString(TYPE_ATTRIB, type.AssemblyQualifiedName);
            //new XmlSerializer(type).Serialize(writer, _data);

            writer.WriteAttributeString(TYPE_ATTRIB, type.AssemblyQualifiedName);
            writer.WriteAttributeString(NAME_ATTRIB, Data.Name);
            writer.WriteElementString(DATA_ELEMENT, null, Data.Data);
        }
        

        #endregion

        // Override the Implicit Conversions Since the XmlSerializer
        // Casts to/from the required types implicitly.
        public static implicit operator T(ConnectionXmlSerializer<T> o)
        {
            return o.Data;
        }

        public static implicit operator ConnectionXmlSerializer<T>(T o)
        {
            return o == null ? null : new ConnectionXmlSerializer<T>(o);
        }
    }
}
